package com.uziot.bucket.dynamic.config;

import com.uziot.bucket.dynamic.utils.SpringContextUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Arrays;
import java.util.Objects;

/**
 * @author shidt
 * @version V1.0
 * @className SchedulerRunnable
 * @date 2020-11-21 23:24:52
 * @description 定时任务运行类
 * 可添加定时任务，通过此执行器来执行定时任务
 */
public class SchedulerRunnable implements Runnable {

    private static final Logger log = LoggerFactory.getLogger(SchedulerRunnable.class);

    private final String beanName;

    private final String methodName;

    private final Object[] params;

    public SchedulerRunnable(String beanName, String methodName) {
        this(beanName, methodName, (Object) null);
    }

    public SchedulerRunnable(String beanName, String methodName, Object... params) {
        this.beanName = beanName;
        this.methodName = methodName;
        this.params = params;
    }

    @Override
    public void run() {
        // 可新增在定时任务执行时获得锁，执行完成释放锁来控制是否并发执行
        this.execute();
    }

    /**
     * 执行方法
     */
    private void execute() {
        log.info("定时任务开始执行 - bean：{}，方法：{}，参数：{}", beanName, methodName, params);
        long start = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
        try {
            Object target = SpringContextUtils.getBean(beanName);
            // 执行方法控制
            Method method;
            if (null != params && params.length > 0) {
                Class<?>[] paramCls = new Class[params.length];
                for (int i = 0; i < params.length; i++) {
                    paramCls[i] = params[i].getClass();
                }
                method = target.getClass().getDeclaredMethod(methodName, paramCls);
            } else {
                method = target.getClass().getDeclaredMethod(methodName);
            }
            // 设定方法访问属性
            ReflectionUtils.makeAccessible(method);
            // 运行对应方法
            if (null != params && params.length > 0) {
                method.invoke(target, params);
            } else {
                method.invoke(target);
            }
        } catch (Exception ex) {
            log.error(String.format("定时任务执行异常 - bean：%s，方法：%s，参数：%s ", beanName, methodName, Arrays.toString(params)), ex);
        }
        long end = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
        String costTime = String.valueOf(end - start);
        log.info("定时任务执行结束 - bean：{}，方法：{}，参数：{}，耗时：{} 毫秒", beanName, methodName, params, costTime);
    }

    /**
     * 重写了equals方法，可在取消定时任务的时候，直接new出对象来取消定时任务
     *
     * @param o obj
     * @return boolean
     */
    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        SchedulerRunnable that = (SchedulerRunnable) o;
        if (params == null) {
            return beanName.equals(that.beanName) &&
                    methodName.equals(that.methodName) &&
                    that.params == null;
        }

        return beanName.equals(that.beanName) &&
                methodName.equals(that.methodName) &&
                Arrays.equals(params, that.params);
    }

    @Override
    public int hashCode() {
        if (params == null) {
            return Objects.hash(beanName, methodName);
        }
        return Objects.hash(beanName, methodName, params);
    }

    public String getBeanName() {
        return beanName;
    }

    public String getMethodName() {
        return methodName;
    }

    public Object[] getParams() {
        return params;
    }
}
